#coding: utf-8

require 'socket'
# require File.dirname(__FILE__) + '/utilisateur.rb'
require 'thread'


TAILLE_TAMPON     = 256
MESSAGE_SIZE      = 300
$logfile          = File.dirname(__FILE__) + "./log.log"
configfile        = File.dirname(__FILE__) + "./jip.conf"
$tab_user         = {}
$tab_dep          = {}
$filesendingport  = "9876"
$mutex            = Mutex.new()

########################################################################
#  gestionMessage(buffer): gere les codes de message reçus
# 
#
#	IN:	buffer:message complet reçu du serveur (CODE_CMD + message(s))
#   RETURN: reponse    : la réponse formatée toTransmit

########################################################################
def gestionMessage(buffer, socket)
  log("****************************GESTION DES MESSAGES****************************",true)
  log("<== MESSAGE RECU: <#{buffer}>")
 
  # Création de la réponse
  tampon = buffer.split(" ",2)

    log("TYPE de MESSAGE #{tampon[0].upcase}",true) 
    case tampon[0].upcase
      when "/ID"
        log("Entre dans la boucle de ID",true)       
          if (tampon[1]!= nil)
            if (tampon[1].length >= MESSAGE_SIZE)
              reponse = "402" #message trop long
            else
			  $tab_user[socket] = tampon[1]
              reponse = "200" # traitement cas nominal
            end
          else
            reponse = "404" # Erreur inconnue
          end


         
      when "/DEPENDANCES"
        log("Entre dans la boucle de DEPENDANCES",true)       
          if (tampon[1]!= nil)
            if (tampon[1].length >= MESSAGE_SIZE)
              reponse = "402" #message trop long
            else
				if  ($tab_dep[$tab_user[socket]] == nil)
					$tab_dep[$tab_user[socket]] = []
				end
				
				$tab_dep[$tab_user[socket]] << tampon[1]
				
              reponse = "200" # traitement cas nominal
            end
          else
            reponse = "404" # Erreur inconnue
          end
        
      when "/ANNOTATIONS"
        log("Entre dans la boucle de ANNOTATIONS",true)       
          if (tampon[1]!= nil)
            if (tampon[1].length >= MESSAGE_SIZE)
              reponse = "402" #message trop long
            else
              reponse = "200" # traitement cas nominal
            end
          else
            reponse = "404" # Erreur inconnue
          end
		  
      when "/QUIT"
        log("Entre dans la boucle de QUIT",true)       
          if (tampon[1]!= nil)
            if (tampon[1].length >= MESSAGE_SIZE)
              reponse = "402" #message trop long
            else
              reponse = "200" # traitement cas nominal
            end
          else
            reponse = "404" # Erreur inconnue
          end		  
		
      when "/SEND"
        log("Entre dans la boucle de SEND",true)       
        tamponFic = buffer.split(" ")    
        pseudoMp = tamponFic[1] 
		
        if (user.pseudo==pseudoMp)
          reponse = "803:: Vous ne pouvez pas vous envoyer des fichiers"
		elsif (pseudoEstPresent?(pseudoMp) )
		  indice = getIdUser(pseudoMp)
		  socketDesti = getSocket(pseudoMp).to_s
          
		  if user.ajouterFichier(socketDesti,tamponFic[2]) #envoi du fichier au destinataire
            reponse = "806:: message envoyé"
            reponseMp = "850::#{user.pseudo}::#{tamponFic[2]}::#{$filesendingport}"
            toTransmit = "fichier"
          else #sinon envoi d'un fichier déjà en cours

            reponse = "805:: vous ne pouvez pas envoyer plusieurs fichiers à la fois à un destinataire"
          end
        else #sinon c'est une acceptation
          if !lefichier=$tab_user[indice].supprimerFichier(user.socket.to_s) # s'il n'y a pas ce fichier à envoyer au client
            reponse = "807:: Aucun fichier en attente"
            
          else #sinon on accepte la demande
            puts "envoi est accepté"
            reponse = "806:: message envoyé"
            reponseMp = "800::#{user.pseudo}::#{tamponFic[2]}::#{$filesendingport}"

            toTransmit = "fichier"
          end
        end
      # else
        # reponse = "801:: <#{pseudoMp}> non connectés"
      # end
		
 
      else
        reponse = "401" #::Requete inconnue : #{buffer}
	  
        
    end # case
    log("==> MESSAGE RENVOYE ")
    log(reponse)

  return reponse
end


########################################################################
#  pseudoEstPresent?(pseudoAChercher): vérifie si le pseudo existe déjà
#
#   RETURN: BOOLEAN    : True ==> le pseudo existe
########################################################################
def pseudoEstPresent?(pseudoAChercher)
	present = false
	$tab_user.each {|client| present = true if client.pseudo.upcase==pseudoAChercher.upcase}
	return present
end


########################################################################
#  getSocket(pseudoAChercher): retourne la socket d'un utilisateur 
#                              demandé
#                              sinon renvoi nil
########################################################################
def getSocket(pseudoAChercher)
	socket=nil
	$tab_user.each {|client| socket = client.socket if client.pseudo.upcase==pseudoAChercher.upcase}
	return socket
end

########################################################################
#  getIdUser(pseudoAChercher): retourne l'indice d'un utilisateur 
#                              demandé
#                              sinon renvoi nil
########################################################################
def getIdUser(pseudoAChercher)
	parcours = nil
	$tab_user.each {|client| parcours = $tab_user.index(client) if client.pseudo.upcase==pseudoAChercher.upcase}
	return parcours
end

########################################################################
#  chargementInit(configfile): Chargement du fichier de config
#                              
#   MAJ des parametres: $port et $niveauDebugMax 
#   Par défaut : 
#      $port = "1978"
#      $logfile = "./log.log"
#      $niveauDebugMax = false  : signifie que les logs ne sont pas verbeux
########################################################################
def chargementInit(configfile)
  $port = "3007"
  $niveauDebugMax = false
  tampon = nil
  File.open(configfile, File::RDONLY) do |fic|
    fic.each_line do|line|
    # log("INIT: #{line}")
      puts("INIT: #{line}")
      tmp = line.split("=",2)
      
      $port = tmp[1].chomp if (tmp[0].upcase=="PORT")
      $filesendingport = tmp[1].chomp if (tmp[0].upcase=="FILESENDINGPORT")
      $logfile = tmp[1].chomp.delete "\"" if (tmp[0].upcase=="LOGFILE")     # le delete permet de supprimer les ""

      if (tmp[0].upcase=="DEBUG") 
        $niveauDebugMax = true if (tmp[1].chomp=="ON")
      end
    end
  end 
end

########################################################################
#  log(message,traceDebug=false): enregistre des traces
#                              
#   message est le message à afficher (précédé de la date et l'heure)
#   traceDebug active les traces "détails" si le niveau de débug Max est activé
########################################################################
def log(message,traceDebug=false)
  if ( $niveauDebugMax == false && traceDebug == true)
    # pas de logs car niveau de débug bas    
  else
    $mutex.lock
    $log.puts("[#{Time.now}] #{message}")
    $mutex.unlock
  end
end


########################################################################
#  Main
########################################################################      
begin
  chargementInit(configfile) 
  puts $logfile
  $log = File.open($logfile, "w")  

  log("****************************DEMARAGE DU SERVEUR****************************")
  log("port ==> #{$port} et niveauDebugMax ==> #{$niveauDebugMax}")
    
  # Vérification de la ligne de commande de l'appel
  raise "\nUsage: #{$0} [port]\n" if ARGV.length > 2 
  $port = ARGV[0] if (ARGV.length == 1)

  # Ouverture du port
  server = TCPServer.open($port)

  # Ouverture en ecriture du fichier log
  log("Lancement du serveur")

  # Mise en place d'un gestionnaire pour capturer SIGINT (CTRL + C)
  trap("SIGINT") do
      server.close
      server = nil   # pour le ensure
      log("Arrêt du serveur")
      $log.close
      exit 0
  end

  puts "en attente de la connexion d'un client..."
  
 
  # En attente de connexion
  while true
    # $tab_user << Utilisateur.new(server.accept)
	connection = server.accept
 
    Thread.start(connection) do |user|
		connexion = true
      print("<" + user.pseudo + "> connexion acceptée\n")
      log("<" + user.pseudo + "> connexion acceptée")

     
      # Lance l'écoute
      while (connexion && message = user.socket.gets.chomp)
         log("\n__________________________________________________________________")
        reponse,aTransmettre,pseudoMp,reponseMp = gestionMessage(message,user)
        
        log("message transmis à #{aTransmettre}")
          
        # Envoi de la réponse à 1 ou plusieurs client(s)
        case aTransmettre
          when "tous"
              $tab_user.each do |client| 
                log("STATUT <#{client.pseudo}> = #{client.statut}",true)
                client.socket.puts(reponse) if (client.statut=="U")
                log("<#{client.pseudo}> " + reponse)
              end
             
          when "emetteur"
              user.socket.puts(reponse)
              log("<#{user.pseudo}> " + reponse)
            
          when "prive"
              log("message transmis à <#{user.pseudo}> et <#{pseudoMp}>")
              user.socket.puts(reponse)
              log("<#{user.pseudo}> " + reponse)
              getSocket(pseudoMp).puts(reponseMp) if (pseudoMp != "")
              log("<#{pseudoMp}> " + reponseMp)
              
          when "quit"
              $tab_user.each do |client| 
                client.socket.puts(reponse) if client.statut=="U" && client.pseudo != user.pseudo #|| client.pseudo=user.pseudo
                log("<#{client.pseudo}> " + reponse)
              end
          
				connexion = false
          
          when "fichier"
            user.socket.puts(reponse)
            getSocket(pseudoMp).puts(reponseMp) if (pseudoMp != "")
          else #fichier
            user.socket.puts("Erreur fatale")
        end # case
    

      end
      print(user.pseudo, ": fin de connexion\n")
      user.socket.close  
	  
	  $tab_user.delete(user)
    end
  end


rescue Exception => e
  STDERR.puts e
  raise e
ensure
  server.close if server
  $log.close if $log and not $log.closed?
end



